﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Beetle;

namespace Beetle.DimaHead
{
    public class DimaBrain : Brain
    {
        public DimaNeuro neuro;

        public Bettle.Leg[] MakeStep(Bettle bettle, Target[] targets)
        {
            double[] input = MakeInput(bettle, targets[0]);
            double[] output = neuro.MakeIteration(input);
            return MakeOutputLegs(bettle, output);
        }

        public Bettle.Leg[] MakeStepWithLearning(Bettle bettle, Target[] targets)
        {
            double[] input = MakeInput(bettle, targets[0]);
            double[] output = neuro.MakeIteration(input);
            Bettle.Leg[] NewLegs = MakeOutputLegs(bettle, output);
            var Dopozition = bettle.position;
            var NewPosition = bettle.HypMove(NewLegs);
            var tt = Dopozition;
            tt = NewPosition;
            if (IsGoodMove(bettle, NewLegs, targets[0]))
            {
                var bestoutput = MakeTeachingOutput(output);
                if (TeachPosl.Count < Coeff.viborkaCount && TeachPosl.Where(h => h.input == input).Count() == 0) TeachPosl.Add(new Viborka { input = input, output = bestoutput });
                neuro.Teach(bestoutput);
            }
            else
            {
                neuro.Teach(RandomOutput(),Coeff.kbad);
            }

            q++;
            return NewLegs;
        }

        #region Error calc

        public class Viborka
        {
            public double[] input;
            public double[] output;
        }

        private List<double> errors = new List<double>();

        public List<double> ErrorList
        {
            get
            {
                return errors;
            }
            set
            {
                errors = value;
            }
        }

        public List<Viborka> TeachPosl = new List<Viborka>();

        int q = 0;

        public double CalcError()
        {
            if (TeachPosl.Count >= Coeff.viborkaCount && q >= Coeff.q)
            {
                double errorSum = 0;

                foreach (var vibor in TeachPosl)
                {
                    neuro.MakeIteration(vibor.input);
                    errorSum += neuro.Error(vibor.output);
                    //neuro.Teach(vibor.output);
                }

                errors.Add(errorSum);

                TeachPosl.RemoveAt(Coeff.rand.Next(0, TeachPosl.Count - 1));
                q = 0;

                return errorSum;
            }

            return -1;
        }

        #endregion

        #region Мысление

        public double[] RandomOutput()
        {
            double[] result = new double[26];

            for (int i = 0; i < result.Count(); i++)
            {
                result[i] = (double)Coeff.rand.Next(-10, 10) / 10;
            }

            return result;
        }

        public double[] MakeInput(Bettle bettle, Target target)
        {
            double[] result = new double[26];

            int counter = 0;
            for (int i = 0; i < 6; i++)
            {
                if (bettle.Legs[i].IsUp == 0)
                {
                    result[counter + bettle.Legs[i].State] = -0.5;
                }
                else
                {
                    result[counter + bettle.Legs[i].State] = 0.5;
                }

                counter += 3;
            }

            double angleToTarget = bettle.AngleToTarget(target);

            if (angleToTarget >= 0 && angleToTarget < 45) result[counter] = 0.5;
            if (angleToTarget >= 45 && angleToTarget < 90) result[counter + 1] = 0.5;
            if (angleToTarget >= 90 && angleToTarget < 135) result[counter + 2] = 0.5;
            if (angleToTarget >= 135 && angleToTarget < 180) result[counter + 3] = 0.5;
            if (angleToTarget >= 180 && angleToTarget < 225) result[counter + 4] = 0.5;
            if (angleToTarget >= 225 && angleToTarget < 270) result[counter + 5] = 0.5;
            if (angleToTarget >= 270 && angleToTarget < 315) result[counter + 6] = 0.5;
            if (angleToTarget >= 315 && angleToTarget < 360) result[counter + 7] = 0.5;

            return result;
        }

        public Bettle.Leg[] MakeOutputLegs(Bettle bettle, double[] output)
        {
            Bettle.Leg[] result = new Bettle.Leg[6];

            int counter = 0;

            for (int i = 0; i < 6; i++)
            {
                Bettle.Leg tempLeg = new Bettle.Leg();

                tempLeg.Side = i / 3;

                if (Math.Abs(output[counter]) > Coeff.betta && Math.Abs(output[counter]) >= Math.Abs(output[counter + 1]))
                {
                    if (output[counter] > 0) tempLeg.State = bettle.Legs[i].State + 1;
                    else tempLeg.State = bettle.Legs[i].State - 1;
                }

                if (Math.Abs(output[counter + 1]) > Coeff.betta && Math.Abs(output[counter + 1]) > Math.Abs(output[counter]))
                {
                    if (output[counter + 1] > 0) tempLeg.State = bettle.Legs[i].State + 2;
                    else tempLeg.State = bettle.Legs[i].State - 2;
                }

                if (output[counter + 2] >= 0) tempLeg.IsUp = 1;
                else tempLeg.IsUp = 0;

                result[i] = tempLeg;

                counter += 3;
            }

            return result;
        }

        public double[] MakeOutput(Bettle.Leg[] legs)
        {
            double[] result = new double[18];

            int counter = 0;

            for (int i = 0; i < 6; i++)
            {
                if (legs[i].IsUp == 0)
                {
                    result[counter + legs[i].State] = -1;
                }
                else
                {
                    result[counter + legs[i].State] = 1;
                }

                counter += 3;
            }

            return result;
        }

        public double[] MakeTeachingOutput(double[] output)
        {
            double[] result = new double[output.Count()];



            int counter = 0;

            for (int i = 0; i < 6; i++)
            {
                if (output[counter] > 0) result[counter] = 0.1;
                else result[counter] = -0.1;
                if (output[counter + 1] > 0) result[counter + 1] = 0.1;
                else result[counter + 1] = -0.1;
                if (output[counter + 2] > 0) result[counter + 2] = 0.1;
                else result[counter + 2] = -0.1;

                if (Math.Abs(output[counter]) > Coeff.betta && Math.Abs(output[counter]) >= Math.Abs(output[counter + 1]))
                {
                    if (output[counter] > 0) result[counter] = 0.5;
                    else result[counter] = -0.5;
                }

                if (Math.Abs(output[counter + 1]) > Coeff.betta && Math.Abs(output[counter + 1]) > Math.Abs(output[counter]))
                {
                    if (output[counter + 1] > 0) result[counter + 1] = 0.5;
                    else result[counter + 1] = -0.5;
                }

                if (output[counter + 2] >= 0) result[counter + 2] = 0.5;
                else result[counter + 2] = -0.5;

                counter += 3;

            }

            return result;
        }

        public double MagikVektor(Position position, Target target)
        {
            double A1 = Math.Cos(position.ViewAngle * (Math.PI / 180));
            double B1 = -Math.Sin(position.ViewAngle * (Math.PI / 180));
            double A2 = target.coord.y - position.y;
            double B2 = target.coord.x - position.x;

            double A = A2 - A1;
            double B = B2 - B1;

            double Length = Math.Sqrt(Math.Pow(A, 2) + Math.Pow(B, 2));

            return Length;
        }

        public bool IsGoodMove(Bettle bettle, Bettle.Leg[] newLegs, Target target)
        {
            if (MagikVektor(bettle.position, target) > MagikVektor(bettle.HypMove(newLegs), target)) return true;
            else return false;
        } //Хорошое ли движение

        #endregion

        public DimaBrain()
        {
            neuro = new DimaNeuro();
        }

        public DimaBrain(string fileName)
        {
            neuro = new DimaNeuro(fileName);
        }

        public DimaBrain(string fileName,string errorFileName)
        {
            neuro = new DimaNeuro(fileName);

            var strings = System.IO.File.ReadAllLines(@"c:\logs\" + errorFileName);

            List<double> temp = new List<double>();

            foreach (var str in strings)
            {
                temp.Add(double.Parse(str.Replace(".", ",")));
            }

            errors = temp;
        }
    }
}
